package com.shen.yun.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.shen.yun.gameRule.ThreeCompare;
import com.shen.yun.vo.Room;
import com.shen.yun.vo.SocketMsg;
import com.shen.yun.vo.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

@ServerEndpoint(value = "/chat/{nickname}/{roomId}/{allowedPlayerNum}")
@Component
public class ChatWebSocket {
    //房间map,房间号对应的房间
    public static volatile Map<String,Room> roomMap = new HashMap<>();

    //用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArrayList<ChatWebSocket> webSocketSet = new CopyOnWriteArrayList<>();

    //剩余玩家数量
    public static volatile int playerNum;

    //静态变量，用来记录当前房间在线连接数。应该把它设计成线程安全的。
    private static volatile int onlineCount;

    //用来记录昵称和该session进行绑定
    private static Map<String,Session> nameToSessionmap;

    //保存已攻击玩家信息
    private static List<User> AttackedUserSet;

    //已准备下一局的玩家数量
    private static List<String> ReadyUserSet;

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    private String nickname;



    /*
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("nickname") String nickname, @PathParam("roomId") String roomId, @PathParam("allowedPlayerNum") String allowedPlayerNum) throws Exception {

        //若房间号不存在则创建房间
        Room room = roomMap.get(roomId);
        if(room==null){
            room = new Room(roomId, Integer.parseInt(allowedPlayerNum));
            roomMap.put(roomId, room);
        }
        System.out.println("当前共有"+roomMap.size()+"个房间");

        //用来将用户昵称和该session进行绑定
        nameToSessionmap = room.getNameToSessionmap();
        //剩余玩家数量
        playerNum = room.getLeftPlayerNum();

        //静态变量，用来记录当前房间在线连接数。应该把它设计成线程安全的。
        onlineCount = room.getOnlineCount();

        //保存已攻击玩家信息
        AttackedUserSet = room.getAttackedUserSet();

        //已准备下一局的玩家数量
        ReadyUserSet = room.getReadyUserSet();

        this.session = session;
        this.nickname=nickname;

        //进入房间成功
        nameToSessionmap.put(nickname, session);
        webSocketSet.add(this);
        room.setOnlineCount(onlineCount+1);   //房间在线人数+1

        //成功后发送到客户端的消息
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String , Object> sendMap = new HashMap<>();
        sendMap.put("msgType","chat");

        //未加入成功

        if (room.getOnlineCount() > playerNum){
            sendMap.put("content","房间"+roomId+"人数已满，进入房间失败！");
            this.session.getAsyncRemote().sendText(objectMapper.writeValueAsString(sendMap));
            return;
        }
        //加入成功
        System.out.println("有新连接加入:"+nickname+"!当前房间在线人数为" + room.getOnlineCount());
        sendMap.put("content","恭喜您成功连接上WebSocket-->当前房间号"+roomId+"在线人数为："+ room.getOnlineCount());
        this.session.getAsyncRemote().sendText(objectMapper.writeValueAsString(sendMap));

        sendMap.put("content",nickname + "成功连接上WebSocket,-->当前房间号"+roomId+"在线人数为："+ room.getOnlineCount());
        broadcast(objectMapper.writeValueAsString(sendMap), roomId);

        //房间人数已满3人
        if(room.getOnlineCount() == playerNum){
            sendMap.put("content","房间人数已满，可以开始游戏！");
            broadcast(objectMapper.writeValueAsString(sendMap), roomId);

            //将玩家名更新到前端
            Map<String , Object> UpdatePlayerNameMap = new HashMap<>();
            UpdatePlayerNameMap.put("msgType", "UpdatePlayerNameMap");
            int i =0;
            for (String name : nameToSessionmap.keySet()){
                UpdatePlayerNameMap.put("player"+ i, name);
                i++;
            }

            broadcast(objectMapper.writeValueAsString(UpdatePlayerNameMap), roomId);
        }
    }

    /*
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("roomId") String roomId) {
        webSocketSet.remove(this); //从set中删除
        nameToSessionmap.remove(nickname);
        roomMap.get(roomId).setOnlineCount(roomMap.get(roomId).getOnlineCount()-1);
        System.out.println(nickname + "连接关闭！当前房间在线人数为" + roomMap.get(roomId).getOnlineCount());
        if (roomMap.get(roomId).getOnlineCount()==0){
            roomMap.remove(roomId);
        }
        System.out.println("当前共有"+roomMap.size()+"个房间");
    }

    /*
     * 发生错误时调用
     *
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 向某个房间群发自定义消息
     * */
    public void broadcast(String message,@PathParam("roomId") String roomId) throws Exception {
        Thread.sleep(100);
        for(Session item : nameToSessionmap.values()){
            //同步异步说明参考：http://blog.csdn.net/who_is_xiaoming/article/details/53287691
            //this.session.getBasicRemote().sendText(message);//同步
            item.getAsyncRemote().sendText(message);//异步
        }
    }

    /*
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("nickname") String nickname, @PathParam("roomId") String roomId, @PathParam("allowedPlayerNum") String allowedPlayerNum) throws Exception {

        System.out.println("来自客户端的消息-->" +nickname+ ": " + message);

        //从客户端传过来的数据是json数据，所以这里使用jackson进行转换为SocketMsg对象，
        // 然后通过socketMsg的type进行判断是单聊还是群聊，进行相应的处理:
        ObjectMapper objectMapper = new ObjectMapper();
        Map map = objectMapper.readValue(message, Map.class);

        //聊天信息
        if ("chat".equals(map.get("msgType"))){
            SocketMsg socketMsg = objectMapper.readValue(objectMapper.writeValueAsString(map.get("sMsg")),SocketMsg.class);
            Map<String , Object> sendMap = new HashMap<>();
            sendMap.put("msgType","chat");

            if (socketMsg.getType()==1){
                //单聊.需要找到发送者和接受者.
                socketMsg.setFromUser(session.getId());
                Session fromSession = session;
                Session toSession = nameToSessionmap.get(socketMsg.getToUser());

                if (toSession!=null && toSession!=fromSession){
                    sendMap.put("content", nickname + ": " + socketMsg.getMsg());

                    fromSession.getAsyncRemote().sendText(objectMapper.writeValueAsString(sendMap));
                    toSession.getAsyncRemote().sendText(objectMapper.writeValueAsString(sendMap));
                }else {
                    sendMap.put("content", "系统消息：对方不在线或者您输入的频道号不对");
                    fromSession.getAsyncRemote().sendText(objectMapper.writeValueAsString(sendMap));
                }
            }else{
                sendMap.put("content", nickname + ": " + socketMsg.getMsg());
                //群发
                broadcast(objectMapper.writeValueAsString(sendMap), roomId);
            }
        }


        //攻击信息
        if ("gameMsg".equals(map.get("msgType"))) {
            Map<String , Object> sendMap = new HashMap<>();
            sendMap.put("msgType","gameMsg");

            //已攻击的玩家
            User user = objectMapper.readValue(objectMapper.writeValueAsString(map.get("gMsg")), User.class);

            user.setName(nickname);
            user.setIsCompare(false);
            System.out.println("已攻击玩家信息： " + user.toString());

            AttackedUserSet.add(user);
            System.out.println("userSet现在几个人： " + AttackedUserSet.size() + "-----" + "playerNum为： " + playerNum);

            //当攻击用户数量满足剩余人数时进行判断整合
            if (AttackedUserSet.size() == playerNum) {
                ThreeCompare compare = new ThreeCompare();
                String msg = compare.circleCompare(AttackedUserSet);
                //本回合详情
                sendMap.put("content", msg + "\n" +"请继续下一回合！");
                broadcast(objectMapper.writeValueAsString(sendMap), roomId);

                //更新血量和气(名字：血量)
                Map<String , Object> UpdatePlayerBlood = new HashMap<>();
                UpdatePlayerBlood.put("msgType", "UpdatePlayerBlood");
                for (User item : AttackedUserSet){
                    UpdatePlayerBlood.put(item.getName(), item.getBlood());
                }
                broadcast(objectMapper.writeValueAsString(UpdatePlayerBlood), roomId);
                //更新血量和气(名字：气)
                Map<String , Object> UpdatePlayerQi = new HashMap<>();
                UpdatePlayerQi.put("msgType", "UpdatePlayerQi");
                for (User item : AttackedUserSet){
                    UpdatePlayerQi.put(item.getName(), item.getQi());
                }
                broadcast(objectMapper.writeValueAsString(UpdatePlayerQi), roomId);

                //是否平局的标志位
                boolean hasWinner = false;

                //判断所有玩家血量是否为0
                for (User item : AttackedUserSet){
                    if (item.getBlood() == 0){

                        //向挂掉的人发送消息
                        Map<String , Object> bloodZero = new HashMap<>();
                        bloodZero.put("msgType", "bloodZero");
                        bloodZero.put("content", item.getName() + "挂了, 还剩" + playerNum + "人");
                        Session toSession = nameToSessionmap.get(item.getName());
                        toSession.getAsyncRemote().sendText(objectMapper.writeValueAsString(bloodZero));
                        System.out.println(item.getName() + "挂了, 还剩" + playerNum + "人");

                        synchronized (this){
                            playerNum --;

                            //只剩一个玩家
                            if (playerNum == 1){
                                for (User leftUser : AttackedUserSet){
                                    if (leftUser.getBlood() != 0){
                                        hasWinner =true;
                                        Map<String , Object> readyMsg = new HashMap<>();
                                        readyMsg.put("msgType", "readyMsg");
                                        readyMsg.put("content","Winner is "+ leftUser.getName()+"！！！！！");
                                        broadcast(objectMapper.writeValueAsString(readyMsg), roomId);
                                        ReadyUserSet.clear();
                                        playerNum = Integer.parseInt(allowedPlayerNum);
                                        break;
                                    }
                                }
                                if (hasWinner==false){
                                    Map<String , Object> readyMsg = new HashMap<>();
                                    readyMsg.put("msgType", "readyMsg");
                                    readyMsg.put("content","平局！！！！！");
                                    broadcast(objectMapper.writeValueAsString(readyMsg), roomId);
                                    ReadyUserSet.clear();
                                    playerNum = Integer.parseInt(allowedPlayerNum);
                                }

//                                Map<String , Object> readyMsg = new HashMap<>();
//                                readyMsg.put("msgType", "readyMsg");
//                                readyMsg.put("content","Winner is "+ AttackedUserSet.get(0).getName()+"！！！！！");
//                                broadcast(objectMapper.writeValueAsString(readyMsg), roomId);
//                                ReadyUserSet.clear();
//                                playerNum = Integer.parseInt(allowedPlayerNum);
                            }
                        }

                    }
                }

                AttackedUserSet.clear();
            } else {
                sendMap.put("content", nickname + "，已确认，请等待其他人完成攻击.....");
                broadcast(objectMapper.writeValueAsString(sendMap), roomId);
            }
        }


        //准备按钮
        if ("readyMsg".equals(map.get("msgType"))) {
            ReadyUserSet.add((String) map.get("readyMsg"));
            //所有人都准备
            if (ReadyUserSet.size()== Integer.parseInt(allowedPlayerNum)){
                Map<String , Object> readyMsg = new HashMap<>();
                readyMsg.put("msgType", "readyMsg");
                readyMsg.put("content","");
                broadcast(objectMapper.writeValueAsString(readyMsg), roomId);
                ReadyUserSet.clear();
                playerNum = Integer.parseInt(allowedPlayerNum);
            }
        }

    }

}
